using QStrings in filter and format vecs. (#428)
authortsteven4 <13596209+tsteven4@users.noreply.github.com>
Mon, 2 Dec 2019 16:34:28 +0000 (09:34 -0700)
committerGitHub <noreply@github.com>
Mon, 2 Dec 2019 16:34:28 +0000 (09:34 -0700)
This includes:
our format list entry vecs_t,
out filter list entry fl_vecs_t and
our format interface ff_vecs_t.

Fix validation of options supplied with a format or filter.
Previously unknown options were only detected if all given options
were unknown.  Now any unknown option will be reported, irregardless
of any other options supplied.
The behavoir of ignoring unknown options is unchanged, they are ignored
except for the warning.

defs.h
filter_vecs.cc
filterdefs.h
garmin.cc
inifile.cc
inifile.h
magproto.cc
main.cc
testo.d/bend.test
vecs.cc

diff --git a/defs.h b/defs.h
index 5dee01c2fddd58e2a0d0ddaf3ce6e22bd4569da9..75200c4b0fd656d1882cbf872f98aefbab2bbfd5 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -920,7 +920,7 @@ using ff_exit = void (*)();
 using ff_writeposn = void (*)(Waypoint*);
 using ff_readposn = Waypoint* (*)(posn_status*);
 
-char* get_option(const char* iarglist, const char* argname);
+QString get_option(const QStringList& options, const char* argname);
 
 geocache_type gs_mktype(const QString& t);
 geocache_container gs_mkcont(const QString& t);
@@ -1050,10 +1050,10 @@ struct ff_vecs_t {
   ff_write write;
   ff_exit exit;
   arglist_t* args;
-  const char* encode;
+  QString encode;
   int fixed_encode;
   position_ops_t position_ops;
-  const char* name;            /* dyn. initialized by find_vec */
+  QString name;                /* dyn. initialized by find_vec */
 };
 
 struct style_vecs_t {
@@ -1067,12 +1067,13 @@ void is_fatal(int condition, const char*, ...) PRINTFLIKE(2, 3);
 void warning(const char*, ...) PRINTFLIKE(1, 2);
 void debug_print(int level, const char* fmt, ...) PRINTFLIKE(2,3);
 
-ff_vecs_t* find_vec(const char*, const char**);
-void assign_option(const char* vecname, arglist_t* ap, const char* val);
-void disp_vec_options(const char* vecname, arglist_t* ap);
+ff_vecs_t* find_vec(const QString&);
+void assign_option(const QString& vecname, arglist_t* ap, const char* val);
+void disp_vec_options(const QString& vecname, const arglist_t* ap);
 void disp_vecs();
-void disp_vec(const char* vecname);
-bool validate_args(const char* vecname, arglist_t* ap);
+void disp_vec(const QString& vecname);
+void validate_options(const QStringList& options, const arglist_t* args, const QString& name);
+bool validate_args(const QString& name, const arglist_t* args);
 bool validate_formats();
 void init_vecs();
 void exit_vecs();
index aca6120a239330ee6d5cf129a79c484bf29bbfce..700094a4b2c87e134d680b7bf11e84a071153104 100644 (file)
 #include "gbversion.h"
 #include "inifile.h"
 
+#include <QtCore/QByteArray>   // for QByteArray
 #include <QtCore/QString>      // for QString
 #include <QtCore/QStringList>  // for QStringList
 #include <QtCore/QVector>      // for QVector<>::iterator, QVector
 #include <QtCore/Qt>           // for CaseInsensitive
+#include <QtCore/QtGlobal>     // for qPrintable
 
 #include <algorithm>           // for sort
 #include <cstdio>              // for printf
-#include <cstring>             // for strchr
+
 
 struct fl_vecs_t {
   Filter* vec;
-  const char* name;
-  const char* desc;
+  QString name;
+  QString desc;
 };
 
 ArcDistanceFilter arcdist;
@@ -170,8 +172,7 @@ const QVector<fl_vecs_t> filter_vec_list = {
     &validate,
     "validate",
     "Validate internal data structures"
-  },
-
+  }
 #elif defined (MINIMAL_FILTERS)
   {
     &trackfilter,
@@ -182,18 +183,24 @@ const QVector<fl_vecs_t> filter_vec_list = {
 };
 
 Filter*
-find_filter_vec(const char* const vecname, const char** opts)
+find_filter_vec(const QString& vecname)
 {
-  const QString svecname = QString(vecname).split(',').at(0);
-  int found = 0;
+  QStringList options = vecname.split(',');
+  if (options.isEmpty()) {
+    fatal("A filter name is required.\n");
+  }
+  const QString svecname = options.takeFirst();
 
   for (const auto& vec : filter_vec_list) {
-    if (svecname.compare(vec.name, Qt::CaseInsensitive)) {
+    if (svecname.compare(vec.name, Qt::CaseInsensitive) != 0) {
       continue;
     }
 
-    /* step 1: initialize by inifile or default values */
     arglist_t* args = vec.vec->get_args();
+
+    validate_options(options, args, vec.name);
+
+    /* step 1: initialize by inifile or default values */
     if (args) {
       for (auto ap = args; ap->argstring; ap++) {
         QString qtemp = inifile_readstr(global_opts.inifile, vec.name, ap->argstring);
@@ -209,25 +216,15 @@ find_filter_vec(const char* const vecname, const char** opts)
     }
 
     /* step 2: override settings with command-line values */
-    const char* res = strchr(vecname, ',');
-    if (res) {
-      *opts = res+1;
-
+    if (!options.isEmpty()) {
       if (args) {
         for (auto ap = args; ap->argstring; ap++) {
-          char* opt = get_option(*opts, ap->argstring);
-          if (opt) {
-            found = 1;
-            assign_option(vec.name, ap, opt);
-            xfree(opt);
+          const QString opt = get_option(options, ap->argstring);
+          if (!opt.isNull()) {
+            assign_option(vec.name, ap, CSTR(opt));
           }
         }
       }
-    } else {
-      *opts = nullptr;
-    }
-    if (opts && opts[0] && !found) {
-      warning("'%s' is an unknown option to %s.\n", *opts, vec.name);
     }
 
     if (global_opts.debug_level >= 1) {
@@ -246,7 +243,7 @@ free_filter_vec(Filter* filter)
   arglist_t* args = filter->get_args();
 
   if (args) {
-    for (arglist_t* ap = args; ap->argstring; ap++) {
+    for (auto ap = args; ap->argstring; ap++) {
       if (ap->argvalptr) {
         xfree(ap->argvalptr);
         ap->argvalptr = *ap->argval = nullptr;
@@ -261,7 +258,7 @@ init_filter_vecs()
   for (const auto& vec : filter_vec_list) {
     arglist_t* args = vec.vec->get_args();
     if (args) {
-      for (arglist_t* ap = args; ap->argstring; ap++) {
+      for (auto ap = args; ap->argstring; ap++) {
         ap->argvalptr = nullptr;
       }
     }
@@ -285,9 +282,9 @@ disp_filter_vecs()
 {
   for (const auto& vec : filter_vec_list) {
     printf("   %-20.20s  %-50.50s\n",
-           vec.name, vec.desc);
+           qPrintable(vec.name), qPrintable(vec.desc));
     arglist_t* args = vec.vec->get_args();
-    for (arglist_t* ap = args; ap && ap->argstring; ap++) {
+    for (auto ap = args; ap && ap->argstring; ap++) {
       if (!(ap->argtype & ARGTYPE_HIDDEN))
         printf("         %-18.18s    %-.50s %s\n",
                ap->argstring, ap->helpstring,
@@ -297,16 +294,16 @@ disp_filter_vecs()
 }
 
 void
-disp_filter_vec(const char* vecname)
+disp_filter_vec(const QString& vecname)
 {
   for (const auto& vec : filter_vec_list) {
-    if (case_ignore_strcmp(vec.name, vecname)) {
+    if (vecname.compare(vec.name, Qt::CaseInsensitive) != 0) {
       continue;
     }
     printf("   %-20.20s  %-50.50s\n",
-           vec.name, vec.desc);
+           qPrintable(vec.name), qPrintable(vec.desc));
     arglist_t* args = vec.vec->get_args();
-    for (arglist_t* ap = args; ap && ap->argstring; ap++) {
+    for (auto ap = args; ap && ap->argstring; ap++) {
       if (!(ap->argtype & ARGTYPE_HIDDEN))
         printf("         %-18.18s    %-.50s %s\n",
                ap->argstring, ap->helpstring,
@@ -316,11 +313,11 @@ disp_filter_vec(const char* vecname)
 }
 
 static
-void disp_help_url(const fl_vecs_t& vec, arglist_t* arg)
+void disp_help_url(const fl_vecs_t& vec, const arglist_t* arg)
 {
-  printf("\t" WEB_DOC_DIR "/fmt_%s.html", vec.name);
+  printf("\t" WEB_DOC_DIR "/fmt_%s.html", CSTR(vec.name));
   if (arg) {
-    printf("#fmt_%s_o_%s",vec.name, arg->argstring);
+    printf("#fmt_%s_o_%s", CSTR(vec.name), arg->argstring);
   }
 }
 
@@ -330,10 +327,10 @@ disp_v1(const fl_vecs_t& vec)
   disp_help_url(vec, nullptr);
   printf("\n");
   arglist_t* args = vec.vec->get_args();
-  for (arglist_t* ap = args; ap && ap->argstring; ap++) {
+  for (auto ap = args; ap && ap->argstring; ap++) {
     if (!(ap->argtype & ARGTYPE_HIDDEN)) {
       printf("option\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
-             vec.name,
+             CSTR(vec.name),
              ap->argstring,
              ap->helpstring,
              name_option(ap->argtype),
@@ -357,7 +354,7 @@ disp_filters(int version)
   auto sorted_filter_vec_list = filter_vec_list;
 
   auto alpha = [](const fl_vecs_t& a, const fl_vecs_t& b)->bool {
-    return case_ignore_strcmp(a.desc, b.desc) < 0;
+    return QString::compare(a.desc, b.desc, Qt::CaseInsensitive) < 0;
   };
 
   std::sort(sorted_filter_vec_list.begin(), sorted_filter_vec_list.end(), alpha);
@@ -367,9 +364,9 @@ disp_filters(int version)
   case 1:
     for (const auto& vec : sorted_filter_vec_list) {
       if (version == 0) {
-        printf("%s\t%s\n", vec.name, vec.desc);
+        printf("%s\t%s\n", CSTR(vec.name), CSTR(vec.desc));
       } else {
-        printf("%s\t%s", vec.name, vec.desc);
+        printf("%s\t%s", CSTR(vec.name), CSTR(vec.desc));
         disp_v1(vec);
       }
     }
index cfed0ed94c586ddc299f10c78ef49b37b71bc22f..49efa99c246da4bab68befbbae47cab207cae9f6 100644 (file)
@@ -47,10 +47,10 @@ struct filter_vecs_t {
   arglist_t* args;
 };
 
-Filter* find_filter_vec(const char* const, const char**);
+Filter* find_filter_vec(const QString&);
 void free_filter_vec(Filter*);
 void disp_filters(int version);
-void disp_filter_vec(const char* vecname);
+void disp_filter_vec(const QString& vecname);
 void disp_filter_vecs();
 void init_filter_vecs();
 void exit_filter_vecs();
index f46de66579773d8eef2eac0741e222e9809b7673..09adc9c8de11b9d342a62cdc7e4edca890d6ce73 100644 (file)
--- a/garmin.cc
+++ b/garmin.cc
@@ -342,9 +342,8 @@ static void
 rd_init(const QString& fname)
 {
   if (setjmp(gdx_jmp_buf)) {
-    const char* vec_opts = nullptr;
     const gdx_info* gi = gdx_get_info();
-    gpx_vec = find_vec("gpx", &vec_opts);
+    gpx_vec = find_vec("gpx");
     gpx_vec->rd_init(gi->from_device.canon);
   } else {
     gpx_vec = nullptr;
index 6fbeb7c809752f820919e259b0c90e36887a7673..0c385c2304abea08764e6542e0a3cdc2fc018d80 100644 (file)
@@ -216,7 +216,7 @@ inifile_has_section(const inifile_t* inifile, const char* section)
  */
 
 QString
-inifile_readstr(const inifile_t* inifile, const char* section, const char* key)
+inifile_readstr(const inifile_t* inifile, const QString& section, const QString& key)
 {
   return inifile_find_value(inifile, section, key);
 }
@@ -228,7 +228,7 @@ inifile_readstr(const inifile_t* inifile, const char* section, const char* key)
  */
 
 int
-inifile_readint(const inifile_t* inifile, const char* section, const char* key, int* value)
+inifile_readint(const inifile_t* inifile, const QString& section, const QString& key, int* value)
 {
   const QString str = inifile_find_value(inifile, section, key);
 
index d45cd579af80ee80cb23c1d2ffc7bafbac18d1ce..f428caa93f1a6a38069be000cf0872f46aabc79a 100644 (file)
--- a/inifile.h
+++ b/inifile.h
@@ -46,14 +46,14 @@ bool inifile_has_section(const inifile_t* inifile, const char* section);
        returns a null QString if not found, otherwise a non-null but possibly
        empty Qstring with the value of key ...
  */
-QString inifile_readstr(const inifile_t* inifile, const char* section, const char* key);
+QString inifile_readstr(const inifile_t* inifile, const QString& section, const QString& key);
 
 /*
      inifile_readint:
        on success the value is stored into "*value" and "inifile_readint" returns 1,
        otherwise inifile_readint returns 0
  */
-int inifile_readint(const inifile_t* inifile, const char* section, const char* key, int* value);
+int inifile_readint(const inifile_t* inifile, const QString& section, const QString& key, int* value);
 
 /*
      inifile_readint_def:
index 5968f955f5db127f031f819cb5bd1eb8ddafae64..a0560a082f029fc4d13e4d39ba230f5af757bc05 100644 (file)
@@ -785,8 +785,7 @@ mag_rd_init_common(const QString& portname)
     const char** dlist = os_get_magellan_mountpoints();
     explorist_info = explorist_ini_get(dlist);
     if (explorist_info) {
-      const char* vec_opts = nullptr;
-      gpx_vec = find_vec("gpx", &vec_opts);
+      gpx_vec = find_vec("gpx");
     }
     return;
   }
diff --git a/main.cc b/main.cc
index fd85021be8465369ee46d19f36a70c193bbe72f0..5786b384ca0567c8507e8b2d3037736b7f461178 100644 (file)
--- a/main.cc
+++ b/main.cc
@@ -158,7 +158,7 @@ usage(const char* pname, int shorter)
 }
 
 static void
-spec_usage(const char* vec)
+spec_usage(const QString& vec)
 {
   printf("\n");
   disp_vec(vec);
@@ -206,9 +206,6 @@ run(const char* prog_name)
   Filter* filter = nullptr;
   QString fname;
   QString ofname;
-  const char* ivec_opts = nullptr;
-  const char* ovec_opts = nullptr;
-  const char* fvec_opts = nullptr;
   int opt_version = 0;
   bool did_something = false;
   WaypointList* wpt_head_bak;
@@ -251,7 +248,7 @@ run(const char* prog_name)
 
     if (qargs.at(argn).size() > 1 && (qargs.at(argn).at(1).toLatin1() == '?' || qargs.at(argn).at(1).toLatin1() == 'h')) {
       if (argn < qargs.size()-1) {
-        spec_usage(qPrintable(qargs.at(argn+1)));
+        spec_usage(qargs.at(argn+1));
       } else {
         usage(prog_name,0);
       }
@@ -267,7 +264,7 @@ run(const char* prog_name)
     switch (c) {
     case 'i':
       optarg = FETCH_OPTARG;
-      ivecs = find_vec(CSTR(optarg), &ivec_opts);
+      ivecs = find_vec(optarg);
       if (ivecs == nullptr) {
         fatal("Input type '%s' not recognized\n", qPrintable(optarg));
       }
@@ -277,7 +274,7 @@ run(const char* prog_name)
         warning("-o appeared before -i.   This is probably not what you want to do.\n");
       }
       optarg = FETCH_OPTARG;
-      ovecs = find_vec(CSTR(optarg), &ovec_opts);
+      ovecs = find_vec(optarg);
       if (ovecs == nullptr) {
         fatal("Output type '%s' not recognized\n", qPrintable(optarg));
       }
@@ -407,7 +404,7 @@ run(const char* prog_name)
       break;
     case 'x':
       optarg = FETCH_OPTARG;
-      filter = find_filter_vec(CSTR(optarg), &fvec_opts);
+      filter = find_filter_vec(optarg);
 
       if (filter) {
         filter->init();
index 843376e85fcf1c0b1d24e2debb24125211b1cc4b..bf6e81a8207fdd15c8880a6cf5d68f3254e1af2e 100644 (file)
@@ -1,5 +1,5 @@
 rm -f ${TMPDIR}/bend.*
 
 
-gpsbabel -i gpx -f ${REFERENCE}/route/bend-input.gpx -x bend,distance=25,interpolate,minangle=5 -o gpx -F ${TMPDIR}/bend.gpx
+gpsbabel -i gpx -f ${REFERENCE}/route/bend-input.gpx -x bend,distance=25,minangle=5 -o gpx -F ${TMPDIR}/bend.gpx
 compare ${REFERENCE}/route/bend-expected.gpx ${TMPDIR}/bend.gpx
diff --git a/vecs.cc b/vecs.cc
index 4cd6b8078c707192d4d57173e581e9f409a582d0..ec4b9b4c3abde15a79ed0e9a3440bdc40d7c3821 100644 (file)
--- a/vecs.cc
+++ b/vecs.cc
 
  */
 
-#include <QtCore/QString>
-#include <QtCore/QVector>
+#include <QtCore/QByteArray>    // for QByteArray
+#include <QtCore/QString>       // for QString
+#include <QtCore/QStringList>   // for QStringList
+#include <QtCore/QVector>       // for QVector<>::iterator, QVector
+#include <QtCore/Qt>            // for CaseInsensitive
+#include <QtCore/QtGlobal>      // for qPrintable
 
 #include <algorithm>            // for sort
+#include <cassert>              // for assert
+#include <cctype>               // for isdigit
 #include <cstdio>               // for printf, putchar, sscanf, size_t
 #include <cstdint>
 #include <cstring>              // for strchr, strtok, memset, strlen
-#include <ctype.h>              // for isdigit
 
 #include "defs.h"
 #include "gbversion.h"          // for WEB_DOC_DIR
 
 struct vecs_t {
   ff_vecs_t* vec;
-  const char* name;
+  QString name;
   QString desc;
   QString extensions; // list of possible extensions separated by '/', first is output default for GUI.
-  const char* parent;
+  QString parent;
 };
 
 extern ff_vecs_t an1_vecs;
@@ -1136,12 +1141,12 @@ exit_vecs()
 }
 
 void
-assign_option(const char* module, arglist_t* ap, const char* val)
+assign_option(const QString& module, arglist_t* ap, const char* val)
 {
   const char* c;
 
   if (ap->argval == nullptr) {
-    fatal("%s: No local variable defined for option \"%s\"!", module, ap->argstring);
+    fatal("%s: No local variable defined for option \"%s\"!", qPrintable(module), ap->argstring);
   }
 
   if (ap->argvalptr != nullptr) {
@@ -1172,7 +1177,7 @@ assign_option(const char* module, arglist_t* ap, const char* val)
     } else {
       int test;
       is_fatal(1 != sscanf(c, "%d", &test),
-               "%s: Invalid parameter value %s for option %s", module, val, ap->argstring);
+               "%s: Invalid parameter value %s for option %s", qPrintable(module), val, ap->argstring);
     }
     break;
   case ARGTYPE_FLOAT:
@@ -1181,7 +1186,7 @@ assign_option(const char* module, arglist_t* ap, const char* val)
     } else {
       double test;
       is_fatal(1 != sscanf(c, "%lf", &test),
-               "%s: Invalid parameter value %s for option %s", module, val, ap->argstring);
+               "%s: Invalid parameter value %s for option %s", qPrintable(module), val, ap->argstring);
     }
     break;
   case ARGTYPE_BOOL:
@@ -1205,7 +1210,7 @@ assign_option(const char* module, arglist_t* ap, const char* val)
             c = "1";
           }
         } else {
-          warning(MYNAME ": Invalid logical value '%s' (%s)!\n", c, module);
+          warning(MYNAME ": Invalid logical value '%s' (%s)!\n", c, qPrintable(module));
           c = "0";
         }
         break;
@@ -1224,12 +1229,12 @@ assign_option(const char* module, arglist_t* ap, const char* val)
 }
 
 void
-disp_vec_options(const char* vecname, arglist_t* ap)
+disp_vec_options(const QString& vecname, const arglist_t* ap)
 {
   for (; ap && ap->argstring; ap++) {
     if (*ap->argval && ap->argval) {
       printf("options: module/option=value: %s/%s=\"%s\"",
-             vecname, ap->argstring, *ap->argval);
+             qPrintable(vecname), ap->argstring, *ap->argval);
       if (ap->defaultvalue && (case_ignore_strcmp(ap->defaultvalue, *ap->argval) == 0)) {
         printf(" (=default)");
       }
@@ -1238,37 +1243,45 @@ disp_vec_options(const char* vecname, arglist_t* ap)
   }
 }
 
-ff_vecs_t*
-find_vec(const char* vecname, const char** opts)
+void validate_options(const QStringList& options, const arglist_t* args, const QString& name)
 {
-  char* v = xstrdup(vecname);
-  char* svecname = strtok(v, ",");
-  int found = 0;
+  for (const auto& option : options) {
+    const QString option_name = option.left(option.indexOf('='));
+    bool valid = false;
+    for (auto ap = args; ap && ap->argstring; ap++) {
+      if (option_name.compare(ap->argstring, Qt::CaseInsensitive) == 0) {
+        valid = true;
+        break;
+      }
+    }
+    if (!valid) {
+      warning("'%s' is an unknown option to %s.\n", qPrintable(option_name), qPrintable(name));
+    }
+  }
+}
 
-  if (vecname == nullptr) {
+ff_vecs_t*
+find_vec(const QString& vecname)
+{
+  QStringList options = vecname.split(',');
+  if (options.isEmpty()) {
     fatal("A format name is required.\n");
   }
+  const QString svecname = options.takeFirst();
 
   for (const auto& vec : vec_list) {
-    if (case_ignore_strcmp(svecname, vec.name)) {
+    if (svecname.compare(vec.name, Qt::CaseInsensitive) != 0) {
       continue;
     }
 
-    const char* res = strchr(vecname, ',');
-    if (res) {
-      *opts = strchr(vecname, ',')+1;
-    } else {
-      *opts = nullptr;
-    }
+    validate_options(options, vec.vec->args, vec.name);
 
     if (vec.vec->args) {
       for (auto ap = vec.vec->args; ap->argstring; ap++) {
-        if (res) {
-          const char* opt = get_option(*opts, ap->argstring);
-          if (opt) {
-            found = 1;
-            assign_option(svecname, ap, opt);
-            xfree(opt);
+        if (!options.isEmpty()) {
+          const QString opt = get_option(options, ap->argstring);
+          if (!opt.isNull()) {
+            assign_option(vec.name, ap, CSTR(opt));
             continue;
           }
         }
@@ -1283,9 +1296,6 @@ find_vec(const char* vecname, const char** opts)
         }
       }
     }
-    if (opts && opts[0] && !found) {
-      warning("'%s' is an unknown option to %s.\n", *opts, vec.name);
-    }
 
     if (global_opts.debug_level >= 1) {
       disp_vec_options(vec.name, vec.vec->args);
@@ -1294,7 +1304,6 @@ find_vec(const char* vecname, const char** opts)
 #if CSVFMTS_ENABLED
     // xcsv_setup_internal_style( nullptr );
 #endif // CSVFMTS_ENABLED
-    xfree(v);
     vec.vec->name = vec.name;  /* needed for session information */
     return vec.vec;
 
@@ -1305,25 +1314,18 @@ find_vec(const char* vecname, const char** opts)
    * is to search the list of xcsv styles.
    */
   for (const auto& svec : style_list) {
-    if (case_ignore_strcmp(svecname, svec.name)) {
+    if (svecname.compare(svec.name,  Qt::CaseInsensitive) != 0) {
       continue;
     }
 
-    const char* res = strchr(vecname, ',');
-    if (res) {
-      *opts = strchr(vecname, ',') + 1;
-    } else {
-      *opts = nullptr;
-    }
+    validate_options(options, vec_list[0].vec->args, svec.name);
 
     if (vec_list[0].vec->args) {
       for (auto ap = vec_list[0].vec->args; ap->argstring; ap++) {
-        if (res) {
-          const char* opt = get_option(*opts, ap->argstring);
-          if (opt) {
-            found = 1;
-            assign_option(svecname, ap, opt);
-            xfree(opt);
+        if (!options.isEmpty()) {
+          const QString opt = get_option(options, ap->argstring);
+          if (!opt.isNull()) {
+            assign_option(svec.name, ap, CSTR(opt));
             continue;
           }
         }
@@ -1339,10 +1341,6 @@ find_vec(const char* vecname, const char** opts)
       }
     }
 
-    if (opts && opts[0] && !found) {
-      warning("'%s' is an unknown option to %s.\n", *opts, svec.name);
-    }
-
     if (global_opts.debug_level >= 1) {
       disp_vec_options(svec.name, vec_list[0].vec->args);
     }
@@ -1350,7 +1348,6 @@ find_vec(const char* vecname, const char** opts)
     xcsv_setup_internal_style(svec.style_buf);
 #endif // CSVFMTS_ENABLED
 
-    xfree(v);
     vec_list[0].vec->name = svec.name; /* needed for session information */
     return vec_list[0].vec;
   }
@@ -1358,7 +1355,6 @@ find_vec(const char* vecname, const char** opts)
   /*
    * Not found.
    */
-  xfree(v);
   return nullptr;
 }
 
@@ -1366,44 +1362,31 @@ find_vec(const char* vecname, const char** opts)
  * Find and return a specific argument in an arg list.
  * Modelled approximately after getenv.
  */
-char*
-get_option(const char* iarglist, const char* argname)
+QString
+get_option(const QStringList& options, const char* argname)
 {
-  const size_t arglen = strlen(argname);
-  char* rval = nullptr;
-  char* argp;
+  QString rval;
 
-  if (!iarglist) {
-    return nullptr;
-  }
-
-  char* arglist = xstrdup(iarglist);
-
-  for (char* arg = arglist; argp = strtok(arg, ","), nullptr != argp; arg = nullptr) {
-    if (0 == case_ignore_strncmp(argp, argname, arglen)) {
+  for (const auto& option : options) {
+    int split = option.indexOf('=');
+    const QString option_name = option.left(split);
+    if (option_name.compare(argname, Qt::CaseInsensitive) == 0) {
       /*
        * If we have something of the form "foo=bar"
        * return "bar".   Otherwise, we assume we have
        * simply "foo" so we return that.
        */
-      if (argp[arglen] == '=') {
-        rval = argp + arglen + 1;
+      if (split >= 0) { // we found an '='s.
+        rval = option.mid(split + 1); // not null, possibly empty
+        assert(!rval.isNull());
         break;
-      } else if (argp[arglen] == '\0') {
-        rval = argp;
+      } else {
+        rval = option_name; // not null, possibly empty.
+        assert(!rval.isNull());
         break;
       }
     }
   }
-  /*
-   * Return an offset into the allocated copy.
-   * The caller mustn't free or otherwise get froggy with
-   * this data.
-   */
-  if (rval) {
-    rval = xstrdup(rval);
-  }
-  xfree(arglist);
   return rval;
 }
 
@@ -1417,6 +1400,7 @@ sort_and_unify_vecs()
 {
   QVector<vecs_t> svp;
   svp.reserve(vec_list.size() + style_list.size());
+
   /* Normal vecs are easy; populate the first part of the array. */
   for (const auto& vec : vec_list) {
     vecs_t uvec = vec;
@@ -1434,18 +1418,14 @@ sort_and_unify_vecs()
     uvec.vec = new ff_vecs_t; /* LEAK */
     uvec.extensions = xcsv_file.extension;
     *uvec.vec = *vec_list[0].vec; /* Inherits xcsv opts */
-    /* Reset file type to inherit ff_type from xcsv for everything
-     * except the xcsv format itself, which we leave as "internal"
+    /* Reset file type to inherit ff_type from xcsv. */
+    uvec.vec->type = xcsv_file.type;
+    /* Skip over the first help entry for all but the
+     * actual 'xcsv' format - so we don't expose the
+     * 'full path to xcsv style file' argument to any
+     * GUIs for an internal format.
      */
-    if (case_ignore_strcmp(svec.name, "xcsv")) {
-      uvec.vec->type = xcsv_file.type;
-      /* Skip over the first help entry for all but the
-       * actual 'xcsv' format - so we don't expose the
-       * 'full path to xcsv style file' argument to any
-       * GUIs for an internal format.
-       */
-      uvec.vec->args++;
-    }
+    uvec.vec->args++;
     memset(&uvec.vec->cap, 0, sizeof(uvec.vec->cap));
     switch (xcsv_file.datatype) {
     case unknown_gpsdata:
@@ -1471,7 +1451,7 @@ sort_and_unify_vecs()
    *  parse for help on available command line options.
    */
   auto alpha = [](const vecs_t& a, const vecs_t& b)->bool {
-    return case_ignore_strcmp(a.desc, b.desc) < 0;
+    return QString::compare(a.desc, b.desc, Qt::CaseInsensitive) < 0;
   };
 
   /* Now that we have everything in an array, alphabetize them */
@@ -1490,7 +1470,7 @@ disp_vecs()
     if (vec.vec->type == ff_type_internal)  {
       continue;
     }
-    printf(VEC_FMT, vec.name, CSTR(vec.desc));
+    printf(VEC_FMT, qPrintable(vec.name), qPrintable(vec.desc));
     for (auto ap = vec.vec->args; ap && ap->argstring; ap++) {
       if (!(ap->argtype & ARGTYPE_HIDDEN))
         printf("         %-18.18s    %s%-.50s %s\n",
@@ -1504,15 +1484,15 @@ disp_vecs()
 }
 
 void
-disp_vec(const char* vecname)
+disp_vec(const QString& vecname)
 {
   const auto svp = sort_and_unify_vecs();
   for (const auto& vec : svp) {
-    if (case_ignore_strcmp(vec.name, vecname))  {
+    if (vecname.compare(vec.name, Qt::CaseInsensitive) != 0)  {
       continue;
     }
 
-    printf(VEC_FMT, vec.name, CSTR(vec.desc));
+    printf(VEC_FMT, qPrintable(vec.name), qPrintable(vec.desc));
     for (auto ap = vec.vec->args; ap && ap->argstring; ap++) {
       if (!(ap->argtype & ARGTYPE_HIDDEN))
         printf("         %-18.18s    %s%-.50s %s\n",
@@ -1552,7 +1532,7 @@ disp_v1(ff_type t)
 }
 
 static void
-disp_v2(ff_vecs_t* v)
+disp_v2(const ff_vecs_t* v)
 {
   for (auto& i : v->cap) {
     putchar((i & ff_cap_read) ? 'r' : '-');
@@ -1581,11 +1561,11 @@ name_option(uint32_t type)
 }
 
 static
-void disp_help_url(const vecs_t& vec, arglist_t* arg)
+void disp_help_url(const vecs_t& vec, const arglist_t* arg)
 {
-  printf("\t" WEB_DOC_DIR "/fmt_%s.html", vec.name);
+  printf("\t" WEB_DOC_DIR "/fmt_%s.html", CSTR(vec.name));
   if (arg) {
-    printf("#fmt_%s_o_%s",vec.name, arg->argstring);
+    printf("#fmt_%s_o_%s", CSTR(vec.name), arg->argstring);
   }
   printf("\n");
 }
@@ -1598,7 +1578,7 @@ disp_v3(const vecs_t& vec)
   for (auto ap = vec.vec->args; ap && ap->argstring; ap++) {
     if (!(ap->argtype & ARGTYPE_HIDDEN)) {
       printf("option\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
-             vec.name,
+             CSTR(vec.name),
              ap->argstring,
              ap->helpstring,
              name_option(ap->argtype),
@@ -1639,11 +1619,11 @@ disp_formats(int version)
       if (version >= 2) {
         disp_v2(vec.vec);
       }
-      printf("%s\t%s\t%s%s%s\n", vec.name,
+      printf("%s\t%s\t%s%s%s\n", CSTR(vec.name),
              !vec.extensions.isEmpty() ? CSTR(vec.extensions) : "",
              CSTR(vec.desc),
              version >= 3 ? "\t" : "",
-             version >= 3 ? vec.parent : "");
+             version >= 3 ? CSTR(vec.parent) : "");
       if (version >= 3) {
         disp_v3(vec);
       }
@@ -1654,26 +1634,28 @@ disp_formats(int version)
   }
 }
 
-bool validate_args(const char* vecname, arglist_t* ap)
+bool validate_args(const QString& name, const arglist_t* args)
 {
   bool ok = true;
 
-  for (; ap && ap->argstring; ap++) {
-    if (ap->argtype == ARGTYPE_INT) {
-      if (ap->defaultvalue &&
-          ! is_integer(ap->defaultvalue)) {
-        Warning() << vecname << "Int option" << ap->argstring << "default value" << ap->defaultvalue << "is not an integer.";
-        ok = false;
-      }
-      if (ap->minvalue &&
-          ! is_integer(ap->minvalue)) {
-        Warning() << vecname << "Int option" << ap->argstring << "minimum value" << ap->minvalue << "is not an integer.";
-        ok = false;
-      }
-      if (ap->maxvalue &&
-          ! is_integer(ap->maxvalue)) {
-        Warning() << vecname << "Int option" << ap->argstring << "maximum value" << ap->maxvalue << "is not an integer.";
-        ok = false;
+  if (args != nullptr) {
+    for (auto ap = args; ap->argstring; ap++) {
+      if (ap->argtype == ARGTYPE_INT) {
+        if (ap->defaultvalue &&
+            ! is_integer(ap->defaultvalue)) {
+          Warning() << name << "Int option" << ap->argstring << "default value" << ap->defaultvalue << "is not an integer.";
+          ok = false;
+        }
+        if (ap->minvalue &&
+            ! is_integer(ap->minvalue)) {
+          Warning() << name << "Int option" << ap->argstring << "minimum value" << ap->minvalue << "is not an integer.";
+          ok = false;
+        }
+        if (ap->maxvalue &&
+            ! is_integer(ap->maxvalue)) {
+          Warning() << name << "Int option" << ap->argstring << "maximum value" << ap->maxvalue << "is not an integer.";
+          ok = false;
+        }
       }
     }
   }